#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include "constants.h"
#include "netutils.h"
#include "tklog.h"
#include "tkevent.h"

#define MAX_FD 1000

struct custom_event {
	void *data;			/* point to custom_socket */

	struct custom_event *next;

	/**
        struct rb_node node;
	unsigned long timeout_time;
	**/
	
	/* kinds of flag */
        unsigned accept_ready:1;
	unsigned readable_flag:1;
        unsigned writable_flag:1;
        unsigned active:1;      	/* register in event pool, active will be 1, and or not */
	unsigned active_read:1;
	unsigned active_write:1;
	unsigned active_error:1;
        unsigned timeout:1;     	/* if the event is timeout set 1 */
        unsigned in_timer_set:1;        /* if the event is set in timer, in_timer_set flag will be 1 */
        unsigned shutdown:1;            /* aready close status, now wait for connection's other side's read or write */
        unsigned error:1;

};

void set_epoll_after_connect(struct select_env *env, after_connect_entry after_connect)
{
	env->after_connect = after_connect;
}

/* data is custom_socket */
static struct custom_event* get_custom_event(void *data)
{
	struct custom_event *event = (struct custom_event*)malloc(sizeof(struct custom_event));
	memset(event, 0x00, sizeof(struct custom_event));
	
	event->data = data;

	return event;
}

struct custom_socket* get_custom_socket(int sockfd)
{
	struct custom_socket* s = (struct custom_socket*)malloc(sizeof(struct custom_socket));
	if (!s)
		return NULL;

	memset(s, 0x00, sizeof(struct custom_socket));
	s->event = get_custom_event(s);
	if (!s->event) {
		free(s);
		return NULL;
	}
	
	s->sockfd = sockfd;

	return s;	
}

int set_custom_socket_addr_info_by_sockfd(struct custom_socket* s, int sockfd)
{
        struct sockaddr_in sockaddr;
	socklen_t addrlen;

	addrlen = sizeof(struct sockaddr_in);
        if (getsockname(sockfd, (struct sockaddr *)&sockaddr, &addrlen)) {
                elog("getsockname failed, errno[%d]", errno);
                return -1;
        }

        memset(s->addr.ipv4, 0x00, sizeof(s->addr.ipv4));
        if (inet_ntop(AF_INET, &sockaddr.sin_addr.s_addr, s->addr.ipv4, sizeof(s->addr.ipv4) - 1) == NULL) {
                elog("inet_pton failed");
		return -2;
        }
	
	s->addr.port = ntohs(sockaddr.sin_port);
	memcpy(&s->addr.sockaddr, &sockaddr, sizeof(struct sockaddr_in));

	return 0;
}

int insert_custom_socket(struct select_env *env, struct custom_socket *s)
{
	int sockfd;

	sockfd = s->sockfd;

        if (sockfd >= MAX_FD) {
                elog("excess max fd, [%d]", sockfd);
                return -1;
        }

        if (env->sockets_array[sockfd] != NULL) {
                elog("aready in select env [%d]", sockfd);
                return -2;
	}

        env->sockets_array[sockfd] = s;
	if (sockfd + 1 > env->maxfd_plus_1) {
		env->maxfd_plus_1 = sockfd + 1;
	}

	return 0;
}

int delete_custom_socket(struct select_env *env, struct custom_socket *s)
{
        int sockfd;
	int i;

        sockfd = s->sockfd;

        if (sockfd >= MAX_FD) {
                elog("excess max fd, [%d]", sockfd);
                return -1;
        }

	env->sockets_array[sockfd] = NULL;

	if (env->maxfd_plus_1 == sockfd + 1) {

		env->maxfd_plus_1 = 0;

		for (i = sockfd - 1; i >= 0; i--) {
			if (env->sockets_array[i] != NULL) {
				env->maxfd_plus_1 = i + 1;
			}
		}
	}

        return 0;
}

void destory_custom_socket(struct select_env *env, struct custom_socket *s)
{
	if (s == NULL)
		return;

	struct custom_event *event = (struct custom_event*)s->event;

	if (s->sockfd > 0) {
		select_del_rw_event(env, event);
		close(s->sockfd);
	}

	delete_custom_socket(env, s);
		
	if (s->event) {
		free(s->event);
		s->event = NULL;
	}
	if (s)
		free(s);
}

void* get_custom_socket_pv(struct custom_socket *s)
{
	        return s->pv;
}
void set_custom_socket_pv(struct custom_socket *s, void *pv)
{
	        s->pv = pv;
}

int is_accept(void *pv)
{
	struct custom_event* event = (struct custom_event*)pv;
	return (event->accept_ready == 1);
} 

int is_writable(void *pv)
{
        struct custom_event* event = (struct custom_event*)pv;
        return (event->writable_flag == 1);	
}

int is_readable(void *pv)
{
	struct custom_event* event = (struct custom_event*)pv;
        return (event->readable_flag == 1);
}

void set_readable(void *pv, int flag)
{
	struct custom_event* event = (struct custom_event*)pv;
	event->readable_flag = flag;
}

void set_writable(void *pv, int flag)
{
	struct custom_event *event = (struct custom_event*)pv;
	event->writable_flag = flag;
}

int is_active(void *pv) 
{
	struct custom_event* event = (struct custom_event*)pv;
        return (event->active == 1);
}

int is_timeout(void *pv) 
{
	struct custom_event* event = (struct custom_event*)pv;
        return (event->timeout == 1);
}

int is_intimerset(void *pv)
{
	struct custom_event* event = (struct custom_event*)pv;
        return (event->in_timer_set == 1);
}

int is_shutdown(void *pv)
{
        struct custom_event* event = (struct custom_event*)pv;
        return (event->shutdown == 1);
}

int is_error(void *pv)
{
        struct custom_event* event = (struct custom_event*)pv;
        return (event->error == 1);
}

void set_event_data(void *pv, void *data)
{
        struct custom_event* event = (struct custom_event*)pv;
	event->data = data;
}
void* get_event_data(void *pv)
{
        struct custom_event* event = (struct custom_event*)pv;
	return event->data;
}

/* 设置毫秒数 */
void set_select_env_timeout(struct select_env *env, int millseconds)
{
	env->timeout.tv_sec = millseconds / 1000;
	env->timeout.tv_usec = (millseconds - millseconds/ 1000 * 1000) * 1000;
}

struct select_env* create_select_env(after_connect_entry after_connect)
{
	int len = 0;
	int epollfd;
	struct select_env *env = NULL;

	env = (struct select_env*)malloc(sizeof(struct select_env));
	if (!env) {
		return NULL;
	}

	memset(env, 0x00, sizeof(sizeof(struct select_env)));
	set_select_env_timeout(env, 100);

	env->sockets_array = (struct custom_socket**)malloc(MAX_FD * sizeof(struct custom_socket*));
	if (!env->sockets_array) {
		return NULL;
	}

	FD_ZERO(&env->readfds);
	FD_ZERO(&env->writefds);
	FD_ZERO(&env->errorfds);

	env->after_connect = after_connect;

        return env;
	
}

int destory_select_env(struct select_env *env)
{
	if (env == NULL)
		return 0;

	if (env->sockets_array) {
		free(env->sockets_array);
	}

	free(env);
	
	return 0;
}

/* always return 0 */
int select_add_event(struct select_env *env, void *event, int events)
{
//ilog("------- EVENT INFO START maxfd [%d]-----------",  env->maxfd_plus_1);
	struct custom_event	*ev = NULL;	/* input event */
	struct custom_socket 	*s = NULL;
	int op = 0;
	int needadd = 0;

	ev = (struct custom_event*)event;
	s = (struct custom_socket*)ev->data;

	if (ev->active_write && ev->active_read && ev->active_error)
		return 0;

	if ((events & EVENT_READ) && ev->active_read == 0) {
		ev->active_read = 1;
		FD_SET(s->sockfd, &env->readfds);
	}
	if ((events & EVENT_WRITE) && ev->active_write == 0)  {
		ev->active_write = 1;
		FD_SET(s->sockfd, &env->writefds);
	}

	if (ev->active_error == 0 && (ev->active_read || ev->active_write)) {
		ev->active_error = 1;
		FD_SET(s->sockfd, &env->errorfds);
	}

	ev->active = 1;
//ilog("EVENT sockfd[%d] - add events[%d]", s->sockfd, events);

/*
	int i;
        for (i = 0; i < env->maxfd_plus_1; i++) {
                if (FD_ISSET(i, &env->readfds)) {
                        ilog("sockfd [%d] in read", i);
                }
                if (FD_ISSET(i, &env->writefds)) {
                        ilog("sockfd [%d] in write", i);
                }
                if (FD_ISSET(i, &env->errorfds)) {
                        ilog("sockfd [%d] in error", i);
                }
        }
	ilog("------- EVENT INFO END-----------");
*/
	
	return 0;
}

int select_del_event(struct select_env *env, void *event, int events)
{
	struct custom_event	*ev = NULL;	/* input event */
	struct custom_socket	*s = NULL;
	int op = 0;

	ev = (struct custom_event*)event;
	s = ev->data;
	if (ev->active == 0) 
		return 0;

	if (ev->active_read && (events & EVENT_READ)) {
		ev->active_read = 0;
		FD_CLR(s->sockfd, &env->readfds);
	}

	if (ev->active_write && (events & EVENT_WRITE)) {
		ev->active_write = 0;
		FD_CLR(s->sockfd, &env->writefds);
	}
	
	if (ev->active_write == 0 && ev->active_read == 0) {
		if (ev->active_error) {
			ev->active_error = 0;
			FD_CLR(s->sockfd, &env->errorfds);
		}
		ev->active = 0;
	}
//ilog("EVENT sockfd[%d] - delete events[%d]", s->sockfd, events);
	
	return 0;
}

int select_add_read_event(struct select_env *env, void *event)
{
	return select_add_event(env, event, EVENT_READ);
}

int select_add_write_event(struct select_env *env, void *event)
{
	return select_add_event(env, event, EVENT_WRITE);
}

int select_add_error_event(struct select_env *env, void *event)
{
	return select_add_event(env, event, EVENT_ERROR);
}

int select_del_read_event(struct select_env *env, void *event)
{
	return select_del_event(env, event, EVENT_READ);
}

int select_del_write_event(struct select_env *env, void *event)
{
	return select_del_event(env, event, EVENT_WRITE);
}

int select_del_error_event(struct select_env *env, void *event)
{
	return select_del_event(env, event, EVENT_ERROR);
}

int select_del_rw_event(struct select_env *env, void *event)
{
	return select_del_event(env, event, EVENT_READ | EVENT_WRITE);
}

int select_add_rw_event(struct select_env *env, void *event)
{
	return select_del_event(env, event, EVENT_READ | EVENT_WRITE);
}

int select_set_event(struct select_env* env, int type, void *event)
{
        struct custom_event     *ev = NULL;     /* input event */
        struct custom_socket    *s = NULL;
	int r = 0;

        ev = (struct custom_event*)event;
        s = ev->data;

	if (type == 0) {
		return select_del_rw_event(env, event);
	}

	if ((type & EVENT_READ) && !ev->active_read) {
		if (( r = select_add_read_event(env, event))) {
			return r;
		}
	}
	else if (!(type & EVENT_READ) && ev->active_read) {
                if (( r= select_del_read_event(env, event))) {
			return r;
		}
        }

        if ((type & EVENT_WRITE) && !ev->active_write) {
                if (( r = select_add_write_event(env, event))) {
                        return r;
                }
        }
        else if (!(type & EVENT_WRITE) && ev->active_write) {
                if (( r= select_del_write_event(env, event))) {
                        return r;
                }
        }	

	return 0;
}

int select_process_events(struct select_env* env, long timeout)
{
	int r;
	int events_num;
	struct custom_socket *s = NULL;
	struct custom_event *ev = NULL;
        fd_set readfds;
        fd_set writefds;
        fd_set errorfds;
	int i;
	int fd;

	env->timeout.tv_sec = timeout / 1000;
	env->timeout.tv_usec = (timeout - env->timeout.tv_sec * 1000) * 1000;
	events_num = 0;
	
	FD_ZERO(&readfds);
	FD_ZERO(&writefds);
	FD_ZERO(&errorfds);
	for (i = 0; i < env->maxfd_plus_1; i++) {
		if (FD_ISSET(i, &env->readfds)) {
			FD_SET(i, &readfds);
		}
		if (FD_ISSET(i, &env->writefds)) {
			FD_SET(i, &writefds);
		}
		if (FD_ISSET(i, &env->errorfds)) {
			FD_SET(i, &errorfds);
		}
	}
	r = select(env->maxfd_plus_1, &readfds, &writefds, &errorfds, &env->timeout); 
        if (r == -1) {
                if (r == EINTR) {
			elog("epoll wait eintr");
                        return 0;
		}
                else {
			elog("epoll wait failed, errno[%d]", errno);
                        return -1;
		}
        }
	else if (r == 0) {
		//ilog("timeout");
	}
	else {
		for (i = 0; i < env->maxfd_plus_1; i++) {
			s = env->sockets_array[i];

			if (FD_ISSET(i, &readfds)) {
				events_num++;
				hd_handler_entry(env, s, EVENT_READ);
			}
			if (FD_ISSET(i, &writefds)) {
				events_num++;
				hd_handler_entry(env, s, EVENT_WRITE);
			}
			if (FD_ISSET(i, &errorfds)) {
				events_num++;
				hd_handler_entry(env, s, EVENT_ERROR);
			}
		}
	}
	
	return events_num;
}

